#ifndef _OH_WEAPON_H
#define	_OH_WEAPON_H
/*******************************************************************************
 ** Name: weapon.hpp                                                          **
 ** Description: WorldObject-derived container objects                        **
 **                                                                           **
 ** Open Source Initiative (OSI) Approved License                             **
 **                                                                           **
 ** The contents of this file are subject to the terms of the                 **
 ** Common Development and Distribution License, Version 1.0 only             **
 ** (the "License").  You may not use this file except in compliance          **
 ** with the License.                                                         **
 **                                                                           **
 ** You can find a copy of the license in the LICENSE within                  **
 ** this distribution or at $GOVTSANCT_WIKI/License-software.                 **
 ** Software distributed under the License is distributed on an "AS IS"       **
 ** basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.           **
 ** See the License for the specific language governing permissions           **
 ** and limitations under the License.                                        **
 **                                                                           **
 ** When distributing Covered Code, include this CDDL header in each          **
 ** file and include the License file at $HOME/LICENSE.                       **
 ** If applicable, add the following below this header, with the indicated    **
 ** fields enclosed by brackets "[]" replaced with your own identifying       **
 ** information: Portions Copyright [yyyy] [name of copyright owner]          **
 **                                                                           **
 **                                                                           **
 ** Copyright (c) 2009-2011  Barry Gian James <gian@gamingods.net>            **
 ** All rights reserved.                                                      **
 ******************************************************************************/
// Portions (c) 2012 Open Design Strategies, LLC.
// Modified $Date$ by $Author$
#define _weapon_h_ID    "$Id$"

#include <iostream>
#include <vector>
#include "item.hpp"
#include "traits.hpp"
//#include "client.hpp"
#include "dmg.hpp"

#define AMMO_MAG    'M' 
#define AMMO_BELT   'B' 
#define AMMO_DRUM   'D'
#define AMMO_SINGLE 'S'

enum class AmmoTypeEnum : uint8_t {
    FMJ, JHP, AP, API, HEI, Flechette, LTL, Buckshot,
    Concussion, Blank, Smoke, Flare, Incendiary, CS,
    Sabot, Frangible,
    max
};


//! @class Ammo
//! @brief All ammo information is managed by Ammo objects themselves.
#pragma db object table("ammo")
class Ammo : public Item
{
public:
	SMART_PTR_DECL(Ammo);

	Ammo() : Item() { _atype = AmmoTypeEnum::FMJ;  }
    Ammo(const std::string & s, const std::string & t):Item(ObjectType::Ammo,s,t) { _atype = AmmoTypeEnum::FMJ; }
    Ammo(AmmoTypeEnum,char,int);
    Ammo(const Ammo &);

    static const char *  Type(AmmoTypeEnum);
    static const char *  TypeAbbr(AmmoTypeEnum);

    // Getters
    const AmmoTypeEnum 	AmmoType() const { return _atype; }
//    const char 			Storage() const { return _container; }
//    const ushort 		Rounds() const { return _rounds; }
//    const ushort 		RoundsMax() const { return _rounds_max; }
    const entity_t 		AmmoDmgID() const { return _idammo_dmg; }
    const entity_t 		AmmoTypeID() const { return _idtype; }

    // Setters
    void	AmmoType(const AmmoTypeEnum ate) { _atype = ate; }
//    void	Storage(const char s) { _container = s; }
//    void	Rounds(const ushort r) { _rounds = r; }
//    void	RoundsMax(const ushort r) { _rounds_max = r; }
    void	AmmoDmgID(const entity_t a) { _idammo_dmg = a; }
    void	AmmoTypeID(const entity_t a) { _idtype = a; }

//    void		Reload() { _rounds = _rounds_max; }
//
//    void		FireRound() { --_rounds; }
//    void		FireRound(uint8_t burst) { _rounds -= burst; }
private:
    friend class odb::access;

    AmmoTypeEnum    _atype;
//    char        _container;
//    ushort      _rounds, _rounds_max;
    entity_t	_idammo_dmg;
    entity_t	_idtype;
    Dice		_dmg;
};

//! The two top-level types of damage.
enum class WpnClass :uint8_t { Melee, Ranged, Thrown };
enum class WpnHands { One, Two };
enum class WpnType : uint8_t {
    Improvised, Axe, Mace, Sword, Spear, Nunchuk, Knife,
    Knuckles, Club, ExoticMelee,

    // Maybe break Handgun down into Holdout, Revolver, ACP, etc
    Handgun, AssaultRifle, BoltRifle, SniperRifle, Shotgun,
    Submachinegun, Machinegun, Explosive, Hurled,
    ExoticRanged,
    max
};

enum class WpnFireRate : uint8_t { Safety, Single, Burst, Auto };

// TODO Convert to EnumFlag
// Trait flags
//#define FLAG_TRAIT_WPN_AP        0x00000001	//!< armor piercing
//#define FLAG_TRAIT_WPN_BAL       0x00000002	//!< balanced
//#define FLAG_TRAIT_WPN_DR        0x00000004	//!< damage reduction
//#define FLAG_TRAIT_WPN_DEP       0x00000008	//!< dependable
//#define FLAG_TRAIT_WPN_DUR       0x00000010	//!< durable
//#define FLAG_TRAIT_WPN_ER        0x00000020	//!< extended range
//#define FLAG_TRAIT_WPN_FRL       0x00000040	//!< fast reload
//#define FLAG_TRAIT_WPN_FLA       0x00000080	//!< flamable
//#define FLAG_TRAIT_WPN_FRA       0x00000100	//!< frangible
//#define FLAG_TRAIT_WPN_GUI       0x00000200 //!< guided
//#define FLAG_TRAIT_WPN_LR        0x00000400	//!< long range
//#define FLAG_TRAIT_WPN_NV        0x00000800	//!< nigh vision
//#define FLAG_TRAIT_WPN_OBA       0x00001000	//!< off balance
//#define FLAG_TRAIT_WPN_REL       0x00002000	//!< reliable
//#define FLAG_TRAIT_WPN_SR        0x00004000	//!< short range
//#define FLAG_TRAIT_WPN_SRL       0x00008000	//!< slow reload
//#define FLAG_TRAIT_WPN_SPL       0x00010000
//#define FLAG_TRAIT_WPN_VOL       0x00020000
//#define FLAG_TRAIT_WPN_URL       0x00040000
//#define FLAG_TRAIT_WPN_UPG       0x00080000	//!< upgradable
//#define FLAG_TRAIT_WPN_ACC       0x00100000   // accurate
//#define FLAG_TRAIT_WPN_IMP       0x00200000   // imprecise
//#define FLAG_TRAIT_WPN_BLK       0x00400000   // bulky
//#define FLAG_TRAIT_WPN_DIS       0x00800000   // discreet
//#define FLAG_TRAIT_WPN_SUP       0x01000000   // suppressed
//#define FLAG_TRAIT_WPN_NSY       0x02000000   // noisy
//#define FLAG_TRAIT_WPN_THB       0x04000000   // threaded barrel
//#define FLAG_TRAIT_WPN_CMP       0x08000000   // composite

// To replace the defines above
enum class WpnTraitFlag
{
	ArmorPiercing = 1, Balanced, DmgReduction, Dependable, Durable,
	ExtendedRange, FastReload, Flammable, Frangible, Guided,
	LongRange, NightVision, OffBalance, Reliable, ShortRange,
	SlowReload, SPL, VOL, URL, Upgradable, Accurate, Imprecise,
	Bulky, Discreet, Suppressed, Noisy, ThreadedBarrel, Composite
};

typedef EnumFlag<WpnTraitFlag> WpnTraitFlags;
#pragma db value(WpnTraitFlags) type ("BIGINT")

//! @brief Base class for all weapons in the game.
#pragma db object polymorphic pointer (boost::shared_ptr) table("weapon_base")
class Weapon : public Item
{
public:
	SMART_PTR_DECL(Weapon);

	Weapon();
    Weapon(const std::string &n, const std::string & t);
    Weapon(const std::string&n, const std::string & t,const std::string &,WpnHands,const std::string &,WpnDamage,ObjectType);
    Weapon(const Weapon& orig);
    virtual ~Weapon();

    virtual std::string TypeToStr() { return "Weapon"; }
    bool IsLethal() { if (wdmg == WpnDamage::Blunt) return false; else return true; }


    const std::string &      WeaponType() const { return wtype; }
    const WpnHands     Hands() const { return _hands; }
    const std::string &     WeaponClass() const { return wclass; }
    const WpnDamage    WeaponDamage() const { return wdmg; }
    const ushort   Range() const { return _range; }

    static std::string  SubTypeToStr(WpnType);
    static std::string  HandsToStr(WpnHands h)
    { if (h == WpnHands::One) return "1h"; else return "2h"; }
    static  std::string  WpnClassToStr(WpnClass);
    static  std::string DamageType(WpnDamage dt);

    std::string    FQType();     // Fully-Qualified Object Type

    void     WeaponType(const std::string & wt) { wtype = wt; }
    void     Hands(WpnHands h) { _hands = h; }
    void     WeaponClass(const std::string & c) { wclass = c; }
    void     WeaponDamage(WpnDamage d) { wdmg = d; }
    void     Range(ushort r) { _range = r; }
    
    uint64_t		TraitFlags() { return _wpn_traits.toInt(); }
    void			SetTraitFlag(WpnTraitFlag f) { _wpn_traits.set(f); }
    void			ClearTraitFlag(WpnTraitFlag f) { _wpn_traits.unset(f); }
    void			ZeroTraitFlags() { _wpn_traits.zero(); }

protected:
    friend class odb::access;

#pragma db type ("VARCHAR(32)")
    std::string     wtype;
    WpnHands    _hands;
#pragma db type ("VARCHAR(16)")
    std::string    wclass;
    WpnDamage   wdmg;
    ushort		_error_ceiling;
    ushort		_threat_floor;
    ushort      _range;  //!< range increment (ft)
//    unsigned int	ftraits;
    EnumFlag<WpnTraitFlag> _wpn_traits;

};

//! @class WeaponMagazine
//! @brief Just something to hold rounds in
#pragma db value
struct WeaponMagazine
{
	int		capacity;
	int		current;
	Ammo::pointer	ammo;
};

//! @class RangedWeapon
//! @brief Represents all ranged weapons in the game.
#pragma db object table("weapons_ranged")
class RangedWeapon : public Weapon
{
public:
	RangedWeapon();
	RangedWeapon(const RangedWeapon &);
    RangedWeapon(const std::string & n, const std::string & t); //!< creation via objfactory
    RangedWeapon(const std::string &n, const std::string & t,WpnType,WpnHands,WpnFireRate);

    // Getters
    const ushort	Recoil() const { return _recoil; }
    const WpnFireRate FireRate() const { return _rate; }
    const WpnFireRate	Selector() const { return _selector; }
    const ushort	Recoil2h() const { return _recoil2h; }
    const double 	Rate() const { return _rate_per_turn; }

    ushort	SecondRangeIncr() { return (_range + (_range/2)); }
    ushort	ThirdRangeIncr() { return (_range + _range); }
    ushort	FourthRangeIncr() { return (_range * 2.5); }

    // Setters
    void	Recoil(const ushort r) { _recoil = r; }
    void	FireRate(const WpnFireRate fr) { _rate = fr; }
    void	Selector(const WpnFireRate fr) { _selector = fr; }
    void	Recoil2h(const ushort r) { _recoil2h = r; }
    void	Rate(const double r) { _rate_per_turn = r; }

//    int			Actionable(std::string cmd, Client * player);

    void AddAmmo(WeaponMagazine & a) { ammo.push_back(a); }
private:
    friend class odb::access;

    ushort      _recoil;     //!< str must be >=, and +5 > for full auto
    ushort		_recoil2h;
    WpnFireRate _rate;       //!< what the weapon is capable of doing
    double		_rate_per_turn;	//!< the "real" rate of the weapon in game terms
    WpnFireRate _selector;   //!< The rate of fire selector switch
#pragma db value_type("BINARY(255)")
    std::vector<WeaponMagazine>   ammo;
    // upgrade support ~gian
    // -1 = location not available to upgrade, 0 = ok to upgrade but nothing installed
    // # = the upgrade
    short		_rail_upgrade;		// flashlight, m203, shotgun, sling, tripod
    short		_barrel_upgrade;	// heavy barrel, hbar, flash suppressor
    short		_optics_upgrade;	// scope, laser sight, red-dot
    short		_internal_upgrade;	// trigger job, beveled extractor, etc
    short		_external_upgrade;	// stocks, 2h grip, etc
    
};

//! @class MeleeWeapon
//! @brief Represents all hand-held/melee weapons.
#pragma db object table("weapons_melee")
class MeleeWeapon : public Weapon
{
public:
	MeleeWeapon();
	MeleeWeapon(const MeleeWeapon &);
    MeleeWeapon(const std::string & n, const std::string & t);  //!< creation via objfactory
    MeleeWeapon(const std::string & n, const std::string & t,WpnType wt,WpnHands h,WpnDamage wd,int s,int d);

    // Getters
    const int   MinSTR() const { return _min_str; }
    const int   MinDEX() const { return _min_dex; }
//    int			Actionable(std::string cmd, Client * player);
    const double 	Speed() const { return _speed; }

    // Setters
    void	MinSTR(const int m) { _min_str = m; }
    void	MinDex(const int m) { _min_dex = m; }
    void	Speed(const double s) { _speed = s; }

    const Dice &	Damage() const { return _dmg; }
    void	Damage(const int n, const int s, const int b = 0) {
    	_dmg.num = n; _dmg.sides = s; _dmg.bonus = b;
    }

private:
    friend class odb::access;

    int     _min_str; //!< minimum STR to use
    int     _min_dex; /*!< minimum DEX to use */
    double	_speed;
    Dice	_dmg;

    short _external_upgrade;
    short _hilt_upgrade;
    short _body_upgrade;
};

#endif	/* _OH_WEAPON_H */

